---
title: Type System and Interfaces
---

This module explores Go's type system and interfaces, which are fundamental to Go's design philosophy. Go's type system is statically typed but with some dynamic features through interfaces, providing a balance between safety and flexibility that differs significantly from JavaScript's dynamic typing.

## Go's Type System Overview

Go is a statically typed language, meaning types are checked at compile time. This provides better performance, earlier error detection, and clearer code documentation compared to JavaScript's dynamic typing.

### Static vs Dynamic Typing

<UniversalEditor title="Type System Comparison" compare={true}>
```javascript !! js
// JavaScript: Dynamic typing
let value = 42;           // number
console.log(typeof value); // "number"

value = "hello";          // string
console.log(typeof value); // "string"

value = { name: "John" }; // object
console.log(typeof value); // "object"

// Types are checked at runtime
function processData(data) {
    return data.length; // Works for strings, arrays, but fails for numbers
}

processData("hello"); // 5
processData([1, 2, 3]); // 3
processData(42); // TypeError: data.length is not a function
```

```go !! go
// Go: Static typing
package main

import "fmt"

func main() {
    var value int = 42
    fmt.Printf("Type: %T, Value: %v\n", value, value) // Type: int, Value: 42
    
    // value = "hello" // Compilation error: cannot use "hello" (type string) as int
    
    // Types are checked at compile time
    processData("hello") // Works
    processData([]int{1, 2, 3}) // Works
    // processData(42) // Compilation error: cannot use 42 (type int) as string
}

func processData(data string) {
    fmt.Println(len(data))
}
```
</UniversalEditor>

## Basic Types

Go provides a rich set of basic types that are more explicit than JavaScript's primitive types.

### Numeric Types

<UniversalEditor title="Numeric Types Comparison" compare={true}>
```javascript !! js
// JavaScript: Single number type (64-bit floating point)
let integer = 42;
let float = 3.14;
let bigInt = 9007199254740991n; // BigInt for large integers

console.log(typeof integer); // "number"
console.log(typeof float);   // "number"
console.log(typeof bigInt);  // "bigint"

// All numbers are 64-bit floating point (except BigInt)
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
```

```go !! go
// Go: Multiple numeric types for different use cases
package main

import "fmt"

func main() {
    // Integer types
    var int8Val int8 = 127        // 8-bit signed integer (-128 to 127)
    var int16Val int16 = 32767    // 16-bit signed integer
    var int32Val int32 = 2147483647 // 32-bit signed integer
    var int64Val int64 = 9223372036854775807 // 64-bit signed integer
    var intVal int = 42           // Platform-dependent (32 or 64 bits)
    
    // Unsigned integer types
    var uint8Val uint8 = 255      // 8-bit unsigned integer (0 to 255)
    var uint16Val uint16 = 65535  // 16-bit unsigned integer
    var uint32Val uint32 = 4294967295 // 32-bit unsigned integer
    var uint64Val uint64 = 18446744073709551615 // 64-bit unsigned integer
    var uintVal uint = 42         // Platform-dependent
    
    // Floating point types
    var float32Val float32 = 3.14 // 32-bit floating point
    var float64Val float64 = 3.14159265359 // 64-bit floating point
    
    // Complex types
    var complex64Val complex64 = 3 + 4i  // 64-bit complex number
    var complex128Val complex128 = 3 + 4i // 128-bit complex number
    
    fmt.Printf("int8: %T, uint8: %T, float32: %T\n", int8Val, uint8Val, float32Val)
}
```
</UniversalEditor>

### String and Boolean Types

<UniversalEditor title="String and Boolean Types Comparison" compare={true}>
```javascript !! js
// JavaScript: String and Boolean
let str = "Hello, World!";
let bool = true;

console.log(typeof str);  // "string"
console.log(typeof bool); // "boolean"

// String operations
console.log(str.length);           // 13
console.log(str.toUpperCase());    // "HELLO, WORLD!"
console.log(str.includes("World")); // true

// Boolean operations
console.log(!bool);                // false
console.log(bool && true);         // true
console.log(bool || false);        // true
```

```go !! go
// Go: String and Boolean
package main

import (
    "fmt"
    "strings"
)

func main() {
    var str string = "Hello, World!"
    var boolVal bool = true
    
    fmt.Printf("str type: %T, bool type: %T\n", str, boolVal)
    
    // String operations
    fmt.Println(len(str))                    // 13
    fmt.Println(strings.ToUpper(str))        // "HELLO, WORLD!"
    fmt.Println(strings.Contains(str, "World")) // true
    
    // Boolean operations
    fmt.Println(!boolVal)                    // false
    fmt.Println(boolVal && true)             // true
    fmt.Println(boolVal || false)            // true
    
    // String is immutable in Go
    // str[0] = 'h' // Compilation error: cannot assign to str[0]
}
```
</UniversalEditor>

## Composite Types

Go provides several composite types that allow you to group values together.

### Arrays and Slices

<UniversalEditor title="Arrays and Slices Comparison" compare={true}>
```javascript !! js
// JavaScript: Arrays (dynamic)
let arr = [1, 2, 3, 4, 5];
console.log(arr.length); // 5

// Dynamic resizing
arr.push(6);             // [1, 2, 3, 4, 5, 6]
arr.pop();               // [1, 2, 3, 4, 5]
arr.splice(1, 2);        // [1, 4, 5]

// Array methods
let doubled = arr.map(x => x * 2);     // [2, 8, 10]
let sum = arr.reduce((a, b) => a + b); // 10

// Slicing
let slice = arr.slice(1, 3);           // [4, 5]
```

```go !! go
// Go: Arrays (fixed size) and Slices (dynamic)
package main

import "fmt"

func main() {
    // Arrays: fixed size
    var arr [5]int = [5]int{1, 2, 3, 4, 5}
    fmt.Println(len(arr)) // 5
    
    // Cannot resize arrays
    // arr = append(arr, 6) // Compilation error: cannot use append with array
    
    // Slices: dynamic size
    var slice []int = []int{1, 2, 3, 4, 5}
    fmt.Println(len(slice)) // 5
    
    // Dynamic resizing
    slice = append(slice, 6) // [1, 2, 3, 4, 5, 6]
    slice = slice[:len(slice)-1] // Remove last element: [1, 2, 3, 4, 5]
    
    // Slicing
    subSlice := slice[1:3] // [2, 3]
    
    // Array to slice conversion
    sliceFromArray := arr[:] // Convert array to slice
    
    fmt.Printf("Array: %v, Slice: %v\n", arr, slice)
}
```
</UniversalEditor>

### Maps

<UniversalEditor title="Maps Comparison" compare={true}>
```javascript !! js
// JavaScript: Objects and Maps
let obj = { name: "John", age: 30 };
let map = new Map();
map.set("name", "John");
map.set("age", 30);

console.log(obj.name);           // "John"
console.log(map.get("name"));    // "John"

// Object operations
obj.city = "New York";           // Add property
delete obj.age;                  // Remove property
console.log("name" in obj);      // true

// Map operations
map.set("city", "New York");     // Add key-value
map.delete("age");               // Remove key
console.log(map.has("name"));    // true
```

```go !! go
// Go: Maps
package main

import "fmt"

func main() {
    // Map declaration and initialization
    var m map[string]int = make(map[string]int)
    m["name"] = 1
    m["age"] = 30
    
    // Or using literal syntax
    person := map[string]interface{}{
        "name": "John",
        "age":  30,
        "city": "New York",
    }
    
    // Access values
    fmt.Println(person["name"]) // "John"
    
    // Check if key exists
    if age, exists := person["age"]; exists {
        fmt.Printf("Age: %v\n", age)
    }
    
    // Add/update values
    person["country"] = "USA"
    
    // Delete values
    delete(person, "age")
    
    // Iterate over map
    for key, value := range person {
        fmt.Printf("%s: %v\n", key, value)
    }
}
```
</UniversalEditor>

### Structs

<UniversalEditor title="Structs Comparison" compare={true}>
```javascript !! js
// JavaScript: Objects and Classes
let person = {
    name: "John",
    age: 30,
    city: "New York"
};

// ES6 Classes
class Person {
    constructor(name, age, city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }
    
    greet() {
        return `Hello, I'm ${this.name}`;
    }
}

let john = new Person("John", 30, "New York");
console.log(john.greet()); // "Hello, I'm John"

// Object destructuring
let { name, age } = person;
console.log(name, age); // "John" 30
```

```go !! go
// Go: Structs
package main

import "fmt"

// Struct definition
type Person struct {
    Name string
    Age  int
    City string
}

// Method on struct
func (p Person) Greet() string {
    return fmt.Sprintf("Hello, I'm %s", p.Name)
}

// Method with pointer receiver (can modify struct)
func (p *Person) SetAge(age int) {
    p.Age = age
}

func main() {
    // Create struct instance
    person := Person{
        Name: "John",
        Age:  30,
        City: "New York",
    }
    
    // Access fields
    fmt.Println(person.Name) // "John"
    
    // Call methods
    fmt.Println(person.Greet()) // "Hello, I'm John"
    
    // Modify struct
    person.SetAge(31)
    fmt.Println(person.Age) // 31
    
    // Struct embedding (composition)
    type Employee struct {
        Person
        Salary float64
    }
    
    emp := Employee{
        Person: Person{Name: "Jane", Age: 25, City: "Boston"},
        Salary: 50000,
    }
    
    // Access embedded struct's methods
    fmt.Println(emp.Greet()) // "Hello, I'm Jane"
}
```
</UniversalEditor>

## Interfaces

Interfaces are one of Go's most powerful features, providing a way to define behavior without implementation details.

### Interface Basics

<UniversalEditor title="Interface Basics Comparison" compare={true}>
```javascript !! js
// JavaScript: Duck typing (implicit interfaces)
function processData(data) {
    if (typeof data.length === 'number' && typeof data[0] !== 'undefined') {
        return data.length;
    }
    return 0;
}

// Any object with 'length' property works
console.log(processData([1, 2, 3]));     // 3
console.log(processData("hello"));       // 5
console.log(processData({ length: 10 })); // 10

// ES6 Classes with methods
class Shape {
    area() {
        return 0;
    }
}

class Circle extends Shape {
    constructor(radius) {
        super();
        this.radius = radius;
    }
    
    area() {
        return Math.PI * this.radius ** 2;
    }
}

let circle = new Circle(5);
console.log(circle.area()); // 78.54...
```

```go !! go
// Go: Explicit interfaces
package main

import (
    "fmt"
    "math"
)

// Interface definition
type Measurable interface {
    Area() float64
}

// Struct implementing the interface
type Circle struct {
    Radius float64
}

// Method implementation
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
    Width  float64
    Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Function that works with any type implementing Measurable
func processShape(m Measurable) {
    fmt.Printf("Area: %.2f\n", m.Area())
}

func main() {
    circle := Circle{Radius: 5}
    rectangle := Rectangle{Width: 4, Height: 6}
    
    // Both types implement Measurable interface
    processShape(circle)    // Area: 78.54
    processShape(rectangle) // Area: 24.00
    
    // Interface satisfaction is implicit
    var m Measurable = circle
    fmt.Printf("Type: %T, Area: %.2f\n", m, m.Area())
}
```
</UniversalEditor>

### Interface Composition

<UniversalEditor title="Interface Composition Comparison" compare={true}>
```javascript !! js
// JavaScript: Object composition and mixins
const Movable = {
    move() {
        return "Moving...";
    }
};

const Drawable = {
    draw() {
        return "Drawing...";
    }
};

// Mixin pattern
class GameObject {
    constructor() {
        Object.assign(this, Movable, Drawable);
    }
}

let gameObj = new GameObject();
console.log(gameObj.move());  // "Moving..."
console.log(gameObj.draw());  // "Drawing..."

// Or using composition
class Sprite {
    constructor() {
        this.movable = Movable;
        this.drawable = Drawable;
    }
    
    move() {
        return this.movable.move();
    }
    
    draw() {
        return this.drawable.draw();
    }
}
```

```go !! go
// Go: Interface composition
package main

import "fmt"

// Base interfaces
type Movable interface {
    Move() string
}

type Drawable interface {
    Draw() string
}

// Composed interface
type GameObject interface {
    Movable
    Drawable
}

// Implementation
type Sprite struct {
    X, Y int
}

func (s *Sprite) Move() string {
    s.X += 1
    s.Y += 1
    return fmt.Sprintf("Moving to (%d, %d)", s.X, s.Y)
}

func (s Sprite) Draw() string {
    return fmt.Sprintf("Drawing at (%d, %d)", s.X, s.Y)
}

// Function that works with any GameObject
func updateGameObject(obj GameObject) {
    fmt.Println(obj.Move())
    fmt.Println(obj.Draw())
}

func main() {
    sprite := &Sprite{X: 0, Y: 0}
    updateGameObject(sprite)
    
    // Interface composition allows for flexible design
    var movable Movable = sprite
    var drawable Drawable = sprite
    var gameObj GameObject = sprite
    
    fmt.Println(movable.Move())  // Can only call Move()
    fmt.Println(drawable.Draw()) // Can only call Draw()
    fmt.Println(gameObj.Move())  // Can call both Move() and Draw()
    fmt.Println(gameObj.Draw())
}
```
</UniversalEditor>

### Empty Interface

<UniversalEditor title="Empty Interface Comparison" compare={true}>
```javascript !! js
// JavaScript: Dynamic typing allows any value
function processAnyValue(value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    } else if (typeof value === 'number') {
        return value * 2;
    } else if (Array.isArray(value)) {
        return value.length;
    } else if (typeof value === 'object') {
        return Object.keys(value).length;
    }
    return "Unknown type";
}

console.log(processAnyValue("hello"));     // "HELLO"
console.log(processAnyValue(42));          // 84
console.log(processAnyValue([1, 2, 3]));   // 3
console.log(processAnyValue({a: 1, b: 2})); // 2
```

```go !! go
// Go: Empty interface (interface{}) accepts any type
package main

import (
    "fmt"
    "reflect"
)

// Empty interface can hold any value
func processAnyValue(value interface{}) {
    switch v := value.(type) {
    case string:
        fmt.Printf("String: %s\n", v)
    case int:
        fmt.Printf("Integer: %d\n", v)
    case []int:
        fmt.Printf("Slice: %v (length: %d)\n", v, len(v))
    case map[string]int:
        fmt.Printf("Map: %v (keys: %d)\n", v, len(v))
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

func main() {
    processAnyValue("hello")           // String: hello
    processAnyValue(42)                // Integer: 42
    processAnyValue([]int{1, 2, 3})    // Slice: [1 2 3] (length: 3)
    processAnyValue(map[string]int{"a": 1, "b": 2}) // Map: map[a:1 b:2] (keys: 2)
    
    // Using reflect package for more dynamic behavior
    var value interface{} = "hello"
    fmt.Printf("Type: %v, Value: %v\n", reflect.TypeOf(value), reflect.ValueOf(value))
}
```
</UniversalEditor>

## Type Assertions and Type Switches

Go provides mechanisms to work with interface types dynamically.

### Type Assertions

<UniversalEditor title="Type Assertions Comparison" compare={true}>
```javascript !! js
// JavaScript: Type checking and casting
function processValue(value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    } else if (Array.isArray(value)) {
        return value.join(', ');
    } else if (typeof value === 'object' && value !== null) {
        return JSON.stringify(value);
    }
    return String(value);
}

console.log(processValue("hello"));     // "HELLO"
console.log(processValue([1, 2, 3]));   // "1, 2, 3"
console.log(processValue({a: 1}));      // '{"a":1}'
console.log(processValue(42));          // "42"
```

```go !! go
// Go: Type assertions
package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

func processValue(value interface{}) string {
    // Type assertion with comma ok idiom
    if str, ok := value.(string); ok {
        return str
    }
    
    if arr, ok := value.([]int); ok {
        return fmt.Sprintf("%v", arr)
    }
    
    if m, ok := value.(map[string]int); ok {
        if jsonBytes, err := json.Marshal(m); err == nil {
            return string(jsonBytes)
        }
    }
    
    if num, ok := value.(int); ok {
        return strconv.Itoa(num)
    }
    
    return "unknown type"
}

func main() {
    fmt.Println(processValue("hello"))                    // "hello"
    fmt.Println(processValue([]int{1, 2, 3}))            // "[1 2 3]"
    fmt.Println(processValue(map[string]int{"a": 1}))    // '{"a":1}'
    fmt.Println(processValue(42))                        // "42"
    
    // Type assertion without comma ok (panics if wrong type)
    var value interface{} = "hello"
    str := value.(string) // Safe assertion
    fmt.Println(str)      // "hello"
    
    // This would panic:
    // num := value.(int) // panic: interface conversion
}
```
</UniversalEditor>

### Type Switches

<UniversalEditor title="Type Switches Comparison" compare={true}>
```javascript !! js
// JavaScript: Switch with typeof
function describeType(value) {
    switch (typeof value) {
        case 'string':
            return `String: "${value}"`;
        case 'number':
            return `Number: ${value}`;
        case 'boolean':
            return `Boolean: ${value}`;
        case 'object':
            if (Array.isArray(value)) {
                return `Array with ${value.length} elements`;
            } else if (value === null) {
                return 'Null';
            } else {
                return 'Object';
            }
        default:
            return 'Unknown type';
    }
}

console.log(describeType("hello"));     // "String: \"hello\""
console.log(describeType(42));          // "Number: 42"
console.log(describeType(true));        // "Boolean: true"
console.log(describeType([1, 2, 3]));   // "Array with 3 elements"
console.log(describeType({a: 1}));      // "Object"
```

```go !! go
// Go: Type switches
package main

import "fmt"

func describeType(value interface{}) string {
    switch v := value.(type) {
    case string:
        return fmt.Sprintf("String: %q", v)
    case int:
        return fmt.Sprintf("Integer: %d", v)
    case bool:
        return fmt.Sprintf("Boolean: %t", v)
    case []int:
        return fmt.Sprintf("Slice with %d elements", len(v))
    case map[string]int:
        return fmt.Sprintf("Map with %d keys", len(v))
    case nil:
        return "Nil"
    default:
        return fmt.Sprintf("Unknown type: %T", v)
    }
}

func main() {
    fmt.Println(describeType("hello"))                    // String: "hello"
    fmt.Println(describeType(42))                        // Integer: 42
    fmt.Println(describeType(true))                      // Boolean: true
    fmt.Println(describeType([]int{1, 2, 3}))            // Slice with 3 elements
    fmt.Println(describeType(map[string]int{"a": 1}))    // Map with 1 keys
    fmt.Println(describeType(nil))                       // Nil
    
    // Type switches can also be used for control flow
    var values []interface{} = []interface{}{"hello", 42, true, []int{1, 2}}
    
    for _, value := range values {
        switch v := value.(type) {
        case string:
            fmt.Printf("Processing string: %s\n", v)
        case int:
            fmt.Printf("Processing integer: %d\n", v)
        case bool:
            fmt.Printf("Processing boolean: %t\n", v)
        case []int:
            fmt.Printf("Processing slice: %v\n", v)
        }
    }
}
```
</UniversalEditor>

## Pointers and Memory References

Pointers are a crucial feature in Go that allow programs to directly access and manipulate memory addresses. Unlike JavaScript's automatic memory management, Go provides more precise memory control capabilities, which are essential for systems programming and performance optimization.

### JavaScript vs Go Memory Model Comparison

<UniversalEditor title="Memory Model Comparison" compare={true}>
```javascript !! js
// JavaScript: Automatic memory management
let obj1 = { name: "John", age: 30 };
let obj2 = obj1; // Reference assignment

console.log(obj1.name); // "John"
console.log(obj2.name); // "John"

obj2.name = "Jane";
console.log(obj1.name); // "Jane" - Both variables point to same object
console.log(obj2.name); // "Jane"

// Primitive types are value-passed
let a = 5;
let b = a; // Value copy
b = 10;
console.log(a); // 5 - a unchanged
console.log(b); // 10

// Function parameter passing
function modifyObject(obj) {
    obj.name = "Modified"; // Modifies original object
    obj = { name: "New" }; // Reassignment doesn't affect original
}

function modifyPrimitive(val) {
    val = 100; // Doesn't affect original value
}

let person = { name: "Alice" };
let number = 42;

modifyObject(person);
modifyPrimitive(number);

console.log(person.name); // "Modified" - Object was modified
console.log(number);      // 42 - Original value unchanged
```

```go !! go
// Go: Explicit pointer operations
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // Value types
    person1 := Person{Name: "John", Age: 30}
    person2 := person1 // Value copy
    
    fmt.Println("person1:", person1.Name) // "John"
    fmt.Println("person2:", person2.Name) // "John"
    
    person2.Name = "Jane"
    fmt.Println("person1:", person1.Name) // "John" - person1 unchanged
    fmt.Println("person2:", person2.Name) // "Jane"
    
    // Pointer types
    ptr1 := &person1 // Get address of person1
    ptr2 := ptr1     // Pointer copy, points to same memory
    
    fmt.Println("Via ptr1:", ptr1.Name) // "John"
    fmt.Println("Via ptr2:", ptr2.Name) // "John"
    
    ptr2.Name = "Alice" // Modify through pointer
    fmt.Println("person1:", person1.Name) // "Alice" - Original modified
    fmt.Println("Via ptr1:", ptr1.Name) // "Alice"
    
    // Function parameter passing demonstration
    modifyByValue(person1)     // Pass by value
    modifyByPointer(&person1)  // Pass by pointer
    
    fmt.Println("Final person1:", person1.Name) // "Modified by pointer"
}

// Pass by value - doesn't affect original
func modifyByValue(p Person) {
    p.Name = "Modified by value"
    fmt.Println("Inside value pass:", p.Name) // "Modified by value"
}

// Pass by pointer - affects original
func modifyByPointer(p *Person) {
    p.Name = "Modified by pointer"
    fmt.Println("Inside pointer pass:", p.Name) // "Modified by pointer"
}
```
</UniversalEditor>

### Pointer Basics and Syntax

<UniversalEditor title="Pointer Syntax Fundamentals" compare={true}>
```javascript !! js
// JavaScript: No direct pointer concept
// But references can help understand pointers

let value = 42;
let reference = { value: 42 }; // Use object wrapper to simulate pointer

// Cannot directly get memory address of variable
// console.log(&value); // Syntax error

// Reference operations
function increment(ref) {
    ref.value++; // Modify value through reference
}

increment(reference);
console.log(reference.value); // 43

// Array reference example
let arr1 = [1, 2, 3];
let arr2 = arr1; // Reference assignment

arr2.push(4);
console.log(arr1); // [1, 2, 3, 4] - Original array modified
console.log(arr2); // [1, 2, 3, 4]

// Nullify reference
arr2 = null;
console.log(arr1); // [1, 2, 3, 4] - arr1 still valid
console.log(arr2); // null
```

```go !! go
// Go: Explicit pointer syntax
package main

import "fmt"

func main() {
    // Basic pointer operations
    var value int = 42
    var ptr *int = &value // Get address of value
    
    fmt.Printf("value: %d\n", value)         // 42
    fmt.Printf("address of value: %p\n", &value) // 0x...
    fmt.Printf("ptr stores address: %p\n", ptr)  // 0x... (same as above)
    fmt.Printf("value via ptr: %d\n", *ptr)      // 42 (dereference)
    
    // Modify value through pointer
    *ptr = 100 // Dereference and assign
    fmt.Printf("modified value: %d\n", value) // 100
    
    // Zero value of pointer
    var nilPtr *int
    fmt.Printf("nilPtr: %v\n", nilPtr) // <nil>
    
    // Check if pointer is nil
    if nilPtr == nil {
        fmt.Println("nilPtr is nil")
    }
    
    // Multi-level pointers
    var ptrToPtr **int = &ptr
    fmt.Printf("ptrToPtr points to: %p\n", *ptrToPtr)  // address of ptr
    fmt.Printf("double dereference: %d\n", **ptrToPtr) // 100
    
    // Different types of pointers
    var str string = "Hello"
    var strPtr *string = &str
    
    fmt.Printf("string: %s\n", str)
    fmt.Printf("via pointer: %s\n", *strPtr)
    
    *strPtr = "World"
    fmt.Printf("after modification: %s\n", str) // "World"
}
```
</UniversalEditor>

### Pointers in Function Applications

<UniversalEditor title="Pointer Function Parameters Comparison" compare={true}>
```javascript !! js
// JavaScript: Reference passing and value passing
function swapByValue(a, b) {
    // For primitive types, this won't affect original variables
    let temp = a;
    a = b;
    b = temp;
    console.log(`Inside function: a=${a}, b=${b}`);
}

function swapByReference(obj) {
    // Can modify object properties
    let temp = obj.a;
    obj.a = obj.b;
    obj.b = temp;
}

// Primitive type test
let x = 10, y = 20;
swapByValue(x, y);
console.log(`Outside function: x=${x}, y=${y}`); // x=10, y=20 (unchanged)

// Object reference test
let values = { a: 10, b: 20 };
swapByReference(values);
console.log(`After swap: a=${values.a}, b=${values.b}`); // a=20, b=10

// Array as function parameter
function modifyArray(arr) {
    arr.push(4); // Modifies original array
    arr = [9, 8, 7]; // Reassignment doesn't affect original
}

let myArray = [1, 2, 3];
modifyArray(myArray);
console.log(myArray); // [1, 2, 3, 4] - Only push operation took effect
```

```go !! go
// Go: Pass by value vs pass by pointer
package main

import "fmt"

// Pass by value - doesn't affect original variables
func swapByValue(a, b int) {
    fmt.Printf("Before swap inside: a=%d, b=%d\n", a, b)
    a, b = b, a
    fmt.Printf("After swap inside: a=%d, b=%d\n", a, b)
}

// Pass by pointer - affects original variables
func swapByPointer(a, b *int) {
    fmt.Printf("Before swap inside: a=%d, b=%d\n", *a, *b)
    *a, *b = *b, *a
    fmt.Printf("After swap inside: a=%d, b=%d\n", *a, *b)
}

// Struct pass by value
func modifyPersonByValue(p Person) {
    p.Name = "Modified"
    p.Age = 100
}

// Struct pass by pointer
func modifyPersonByPointer(p *Person) {
    p.Name = "Modified by pointer" // Auto-dereference
    p.Age = 100
    // Equivalent to: (*p).Name = "Modified by pointer"
}

type Person struct {
    Name string
    Age  int
}

func main() {
    // Primitive type test
    x, y := 10, 20
    fmt.Printf("Before swap: x=%d, y=%d\n", x, y)
    
    swapByValue(x, y)
    fmt.Printf("After value pass: x=%d, y=%d\n", x, y) // Unchanged
    
    swapByPointer(&x, &y)
    fmt.Printf("After pointer pass: x=%d, y=%d\n", x, y) // Swapped
    
    // Struct test
    person := Person{Name: "Alice", Age: 25}
    fmt.Printf("Original: %+v\n", person)
    
    modifyPersonByValue(person)
    fmt.Printf("After value pass: %+v\n", person) // Unchanged
    
    modifyPersonByPointer(&person)
    fmt.Printf("After pointer pass: %+v\n", person) // Modified
    
    // Special case with slices and maps
    slice := []int{1, 2, 3}
    modifySlice(slice)
    fmt.Printf("After slice modification: %v\n", slice) // [1, 2, 3, 4] - slice header is value-passed but underlying array is shared
}

func modifySlice(s []int) {
    s[0] = 999 // Modifies slice elements
    s = append(s, 4) // May affect original slice in some cases
}
```
</UniversalEditor>

### Pointers in Struct Methods

<UniversalEditor title="Struct Method Receivers Comparison" compare={true}>
```javascript !! js
// JavaScript: Class methods and this binding
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    
    // Methods automatically bound to instance
    area() {
        return this.width * this.height;
    }
    
    // Modify instance state
    scale(factor) {
        this.width *= factor;
        this.height *= factor;
        return this; // Return this for method chaining
    }
    
    // Get copy
    copy() {
        return new Rectangle(this.width, this.height);
    }
    
    // Comparison method
    equals(other) {
        return this.width === other.width && 
               this.height === other.height;
    }
}

let rect1 = new Rectangle(10, 5);
let rect2 = rect1; // Reference assignment

console.log(rect1.area()); // 50

rect1.scale(2);
console.log(rect1.area()); // 200
console.log(rect2.area()); // 200 - Same object

// Create independent copy
let rect3 = rect1.copy();
rect3.scale(0.5);
console.log(rect1.area()); // 200 - Unaffected
console.log(rect3.area()); // 50
```

```go !! go
// Go: Value receiver vs pointer receiver
package main

import "fmt"

type Rectangle struct {
    Width  float64
    Height float64
}

// Value receiver - doesn't modify original struct
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Value receiver - can't modify original struct
func (r Rectangle) ScaleByValue(factor float64) Rectangle {
    r.Width *= factor  // Only modifies copy
    r.Height *= factor
    return r // Return modified copy
}

// Pointer receiver - can modify original struct
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor  // Modifies original struct
    r.Height *= factor // Go auto-dereferences, equivalent to (*r).Width
}

// Pointer receiver - avoid large struct copy overhead
func (r *Rectangle) String() string {
    return fmt.Sprintf("Rectangle{Width: %.2f, Height: %.2f}", r.Width, r.Height)
}

// Value receiver - for operations that don't need modification
func (r Rectangle) Equals(other Rectangle) bool {
    return r.Width == other.Width && r.Height == other.Height
}

// Pointer receiver - setter method
func (r *Rectangle) SetDimensions(width, height float64) {
    r.Width = width
    r.Height = height
}

func main() {
    rect1 := Rectangle{Width: 10, Height: 5}
    rect2 := rect1 // Value copy
    
    fmt.Printf("rect1: %s, Area: %.2f\n", rect1.String(), rect1.Area())
    
    // Value receiver method - doesn't modify original
    scaledRect := rect1.ScaleByValue(2)
    fmt.Printf("Original rect1: %s\n", rect1.String()) // Unchanged
    fmt.Printf("Scaled copy: %s\n", scaledRect.String())
    
    // Pointer receiver method - modifies original
    rect1.Scale(2)
    fmt.Printf("After Scale rect1: %s\n", rect1.String()) // Changed
    fmt.Printf("rect2: %s\n", rect2.String()) // Unchanged (is a copy)
    
    // Call via pointer
    rectPtr := &Rectangle{Width: 3, Height: 4}
    fmt.Printf("Pointer created: %s\n", rectPtr.String())
    
    rectPtr.SetDimensions(6, 8)
    fmt.Printf("After setting dimensions: %s\n", rectPtr.String())
    
    // Go can automatically handle value and pointer conversions
    rect3 := Rectangle{Width: 1, Height: 1}
    rect3.Scale(5) // Go automatically takes address: (&rect3).Scale(5)
    fmt.Printf("Auto conversion: %s\n", rect3.String())
    
    rectPtr2 := &Rectangle{Width: 2, Height: 2}
    area := rectPtr2.Area() // Go auto-dereferences: (*rectPtr2).Area()
    fmt.Printf("Pointer calling value method: %.2f\n", area)
}
```
</UniversalEditor>

### Pointers and Performance Optimization

<UniversalEditor title="Performance Optimization Comparison" compare={true}>
```javascript !! js
// JavaScript: Automatic optimization, limited developer control
class LargeObject {
    constructor() {
        // Simulate large object
        this.data = new Array(1000).fill(0).map((_, i) => ({
            id: i,
            value: Math.random() * 100,
            timestamp: Date.now(),
            metadata: {
                type: 'sample',
                category: 'test',
                description: 'A sample data point'
            }
        }));
    }
    
    // Calculate sum - V8 engine auto-optimizes
    calculateSum() {
        return this.data.reduce((sum, item) => sum + item.value, 0);
    }
    
    // Filter data - creates new array
    filterByValue(threshold) {
        return this.data.filter(item => item.value > threshold);
    }
    
    // Modify data - in-place modification
    scaleValues(factor) {
        this.data.forEach(item => {
            item.value *= factor;
        });
    }
}

// Performance testing
console.time('Large Object Creation');
let largeObj = new LargeObject();
console.timeEnd('Large Object Creation');

console.time('Sum Calculation');
let sum = largeObj.calculateSum();
console.timeEnd('Sum Calculation');

console.time('Filtering');
let filtered = largeObj.filterByValue(50);
console.timeEnd('Filtering');

console.log(`Sum: ${sum.toFixed(2)}, Filtered count: ${filtered.length}`);
```

```go !! go
// Go: Explicit memory and performance control
package main

import (
    "fmt"
    "time"
)

type DataPoint struct {
    ID        int
    Value     float64
    Timestamp int64
    Metadata  Metadata
}

type Metadata struct {
    Type        string
    Category    string
    Description string
}

type LargeObject struct {
    Data []DataPoint
}

// Value receiver - copies entire struct (poor performance)
func (lo LargeObject) CalculateSumByValue() float64 {
    var sum float64
    for _, item := range lo.Data { // lo.Data is a copy
        sum += item.Value
    }
    return sum
}

// Pointer receiver - avoids copy (better performance)
func (lo *LargeObject) CalculateSum() float64 {
    var sum float64
    for _, item := range lo.Data { // Direct access to original data
        sum += item.Value
    }
    return sum
}

// Pointer receiver - in-place modification, avoids reallocation
func (lo *LargeObject) ScaleValues(factor float64) {
    for i := range lo.Data {
        lo.Data[i].Value *= factor
    }
}

// Return slice instead of copying entire array
func (lo *LargeObject) FilterByValue(threshold float64) []DataPoint {
    var result []DataPoint
    for _, item := range lo.Data {
        if item.Value > threshold {
            result = append(result, item)
        }
    }
    return result
}

// In-place filtering, reuse slice memory
func (lo *LargeObject) FilterInPlace(threshold float64) {
    writeIndex := 0
    for _, item := range lo.Data {
        if item.Value > threshold {
            lo.Data[writeIndex] = item
            writeIndex++
        }
    }
    lo.Data = lo.Data[:writeIndex] // Adjust slice length
}

func NewLargeObject() *LargeObject {
    data := make([]DataPoint, 1000)
    for i := range data {
        data[i] = DataPoint{
            ID:        i,
            Value:     float64(i%100) + 0.5,
            Timestamp: time.Now().UnixNano(),
            Metadata: Metadata{
                Type:        "sample",
                Category:    "test",
                Description: "A sample data point",
            },
        }
    }
    return &LargeObject{Data: data}
}

func main() {
    // Create large object
    start := time.Now()
    largeObj := NewLargeObject()
    fmt.Printf("Object creation time: %v\n", time.Since(start))
    
    // Value receiver performance test
    start = time.Now()
    sumByValue := largeObj.CalculateSumByValue()
    fmt.Printf("Value receiver calculation time: %v\n", time.Since(start))
    
    // Pointer receiver performance test
    start = time.Now()
    sumByPointer := largeObj.CalculateSum()
    fmt.Printf("Pointer receiver calculation time: %v\n", time.Since(start))
    
    fmt.Printf("Result comparison - Value receiver: %.2f, Pointer receiver: %.2f\n", 
               sumByValue, sumByPointer)
    
    // Filter performance test
    start = time.Now()
    filtered := largeObj.FilterByValue(50)
    fmt.Printf("New slice filter time: %v, Result count: %d\n", 
               time.Since(start), len(filtered))
    
    // In-place filter performance test
    originalLen := len(largeObj.Data)
    start = time.Now()
    largeObj.FilterInPlace(50)
    fmt.Printf("In-place filter time: %v, Original length: %d, New length: %d\n", 
               time.Since(start), originalLen, len(largeObj.Data))
    
    // Memory usage demonstration
    fmt.Printf("LargeObject pointer size: %d bytes\n", 
               8) // Pointer size on 64-bit systems
    fmt.Printf("LargeObject value size estimate: %d bytes\n", 
               len(largeObj.Data) * 64) // Rough estimate
}
```
</UniversalEditor>

### Common Pointer Pitfalls and Best Practices

<UniversalEditor title="Pointer Pitfalls and Best Practices" compare={true}>
```javascript !! js
// JavaScript: Common reference-related issues
// 1. Unintended shared references
let originalArray = [1, 2, 3];
let sharedArray = originalArray; // Dangerous: shared reference

sharedArray.push(4);
console.log(originalArray); // [1, 2, 3, 4] - Unintended modification!

// Correct approach: create copies
let safeCopy = [...originalArray]; // Shallow copy
let deepCopy = JSON.parse(JSON.stringify(originalArray)); // Deep copy

// 2. Closure reference traps
let functions = [];
for (let i = 0; i < 3; i++) {
    functions.push(() => console.log(i)); // Correct: let creates block scope
}

functions[0](); // 0
functions[1](); // 1
functions[2](); // 2

// Wrong example (using var)
let badFunctions = [];
for (var j = 0; j < 3; j++) {
    badFunctions.push(() => console.log(j)); // Trap: shares same j
}

badFunctions[0](); // 3
badFunctions[1](); // 3  
badFunctions[2](); // 3

// 3. this binding trap in callbacks
class Counter {
    constructor() {
        this.count = 0;
    }
    
    increment() {
        this.count++;
    }
    
    setupTimer() {
        // Wrong: this is lost
        // setTimeout(this.increment, 1000);
        
        // Correct: use arrow function or bind
        setTimeout(() => this.increment(), 1000);
        // Or: setTimeout(this.increment.bind(this), 1000);
    }
}
```

```go !! go
// Go: Common pointer pitfalls and best practices
package main

import "fmt"

type User struct {
    Name string
    Age  int
}

// Pitfall 1: Returning pointer to local variable
func badCreateUser() *User {
    // Dangerous: returning pointer to stack variable
    user := User{Name: "John", Age: 30}
    return &user // Go compiler will move to heap automatically, but not best practice
}

// Best practice: explicitly use heap allocation
func goodCreateUser() *User {
    return &User{Name: "John", Age: 30} // Create directly on heap
}

// Pitfall 2: Nil pointer dereference
func badProcessUser(user *User) {
    // Dangerous: no nil check
    fmt.Println(user.Name) // Will panic if user is nil
}

func goodProcessUser(user *User) {
    // Best practice: always check for nil
    if user == nil {
        fmt.Println("User is nil")
        return
    }
    fmt.Println(user.Name)
}

// Pitfall 3: Creating pointer slices in loops
func badCreateUserPointers() []*User {
    var users []*User
    names := []string{"Alice", "Bob", "Charlie"}
    
    for _, name := range names {
        user := User{Name: name, Age: 25}
        users = append(users, &user) // Trap: all pointers point to same variable
    }
    return users
}

func goodCreateUserPointers() []*User {
    var users []*User
    names := []string{"Alice", "Bob", "Charlie"}
    
    for _, name := range names {
        // Method 1: use local variable copy
        userName := name
        user := User{Name: userName, Age: 25}
        users = append(users, &user)
        
        // Method 2: create directly on heap
        // users = append(users, &User{Name: name, Age: 25})
    }
    return users
}

// Pitfall 4: Pointer comparison
func demonstratePointerComparison() {
    user1 := &User{Name: "John", Age: 30}
    user2 := &User{Name: "John", Age: 30}
    
    // Trap: pointer comparison compares addresses, not values
    fmt.Printf("Pointers equal: %t\n", user1 == user2) // false
    
    // Correct: compare values
    fmt.Printf("Values equal: %t\n", *user1 == *user2) // true
    
    // Or implement Equals method
    fmt.Printf("Custom comparison: %t\n", user1.Equals(*user2))
}

func (u User) Equals(other User) bool {
    return u.Name == other.Name && u.Age == other.Age
}

// Best practice: method receiver choice
type Counter struct {
    count int
    data  []int // Large data
}

// No modification needed: use value receiver (small structs)
func (c Counter) GetCount() int {
    return c.count
}

// Modification needed: use pointer receiver
func (c *Counter) Increment() {
    c.count++
}

// Large struct: use pointer receiver to avoid copy
func (c *Counter) ProcessData() int {
    var sum int
    for _, v := range c.data {
        sum += v
    }
    return sum
}

// Best practice: error handling with pointers
func findUser(id int) (*User, error) {
    if id <= 0 {
        return nil, fmt.Errorf("invalid user ID: %d", id)
    }
    
    // Simulate database query
    if id == 1 {
        return &User{Name: "Alice", Age: 25}, nil
    }
    
    return nil, fmt.Errorf("user not found: %d", id)
}

func main() {
    fmt.Println("=== Pointer Pitfall Demonstrations ===")
    
    // Demonstrate pitfall 1
    user := badCreateUser()
    fmt.Printf("Bad create user: %+v\n", user)
    
    user = goodCreateUser()
    fmt.Printf("Good create user: %+v\n", user)
    
    // Demonstrate pitfall 2
    fmt.Println("\n=== Nil Pointer Checking ===")
    goodProcessUser(user)
    goodProcessUser(nil)
    
    // Demonstrate pitfall 3
    fmt.Println("\n=== Pointers in Loops ===")
    badUsers := badCreateUserPointers()
    fmt.Println("Wrong implementation:")
    for i, u := range badUsers {
        fmt.Printf("  User %d: %+v\n", i, *u)
    }
    
    goodUsers := goodCreateUserPointers()
    fmt.Println("Correct implementation:")
    for i, u := range goodUsers {
        fmt.Printf("  User %d: %+v\n", i, *u)
    }
    
    // Demonstrate pitfall 4
    fmt.Println("\n=== Pointer Comparison ===")
    demonstratePointerComparison()
    
    // Best practice: error handling
    fmt.Println("\n=== Error Handling ===")
    if user, err := findUser(1); err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Found user: %+v\n", user)
    }
    
    if user, err := findUser(-1); err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Found user: %+v\n", user)
    }
}
```
</UniversalEditor>

### Pointer Best Practices Summary

| Use Case | JavaScript | Go Pointer Best Practice |
|----------|------------|--------------------------|
| **Function Parameters** | Objects auto-passed by reference | Use pointers for large structs, values for small types |
| **Method Receivers** | Auto-bound this | Use pointer receivers for modification or large structs |
| **Performance Optimization** | Engine auto-optimizes | Avoid unnecessary copies, use pointers |
| **Memory Management** | Auto garbage collection | Check for nil, avoid memory leaks |
| **Error Handling** | try-catch or Promise | Return pointer and error, check nil |
| **Comparison Operations** | == compares value or reference | Pointer compares address, dereference to compare values |

**Key Principles:**
1. **Pass small objects by value, large objects by pointer**
2. **Use pointers when modification is needed**
3. **Always check for nil pointers**
4. **Be careful with pointer lifetime in loops**
5. **Use pointers for performance, but prioritize memory safety**

## Type Embedding and Composition

Go uses composition over inheritance, achieved through struct embedding.

<UniversalEditor title="Type Embedding Comparison" compare={true}>
```javascript !! js
// JavaScript: Class inheritance and composition
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        return `${this.name} makes a sound`;
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);
    }
    
    speak() {
        return `${this.name} barks`;
    }
    
    fetch() {
        return `${this.name} fetches the ball`;
    }
}

// Composition
class Walker {
    walk() {
        return "Walking...";
    }
}

class Swimmer {
    swim() {
        return "Swimming...";
    }
}

class Duck {
    constructor(name) {
        this.name = name;
        this.walker = new Walker();
        this.swimmer = new Swimmer();
    }
    
    walk() {
        return this.walker.walk();
    }
    
    swim() {
        return this.swimmer.swim();
    }
}

let dog = new Dog("Rex");
let duck = new Duck("Donald");
console.log(dog.speak()); // "Rex barks"
console.log(duck.walk()); // "Walking..."
```

```go !! go
// Go: Struct embedding (composition)
package main

import "fmt"

// Base struct
type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return fmt.Sprintf("%s makes a sound", a.Name)
}

// Embedding Animal in Dog
type Dog struct {
    Animal // Embedded struct
}

func (d Dog) Speak() string {
    return fmt.Sprintf("%s barks", d.Name)
}

func (d Dog) Fetch() string {
    return fmt.Sprintf("%s fetches the ball", d.Name)
}

// Composition with multiple embedded types
type Walker struct{}

func (w Walker) Walk() string {
    return "Walking..."
}

type Swimmer struct{}

func (s Swimmer) Swim() string {
    return "Swimming..."
}

type Duck struct {
    Animal
    Walker
    Swimmer
}

func main() {
    dog := Dog{Animal{Name: "Rex"}}
    duck := Duck{
        Animal: Animal{Name: "Donald"},
        Walker: Walker{},
        Swimmer: Swimmer{},
    }
    
    fmt.Println(dog.Speak()) // "Rex barks"
    fmt.Println(dog.Fetch()) // "Rex fetches the ball"
    fmt.Println(duck.Walk()) // "Walking..."
    fmt.Println(duck.Swim()) // "Swimming..."
    
    // Interface satisfaction through embedding
    type Speaker interface {
        Speak() string
    }
    
    var speakers []Speaker = []Speaker{dog, duck}
    for _, speaker := range speakers {
        fmt.Println(speaker.Speak())
    }
}
```
</UniversalEditor>

---

### Practice Questions:
1. Explain the difference between Go's static typing and JavaScript's dynamic typing. What are the advantages and disadvantages of each approach?
2. How do Go interfaces differ from JavaScript's duck typing? Provide examples of when each approach is beneficial.
3. Describe Go's struct embedding and how it compares to JavaScript's class inheritance. When would you use each approach?
4. Create a Go program that demonstrates interface composition, type assertions, and type switches with practical examples.
5. **Pointer Exercises**: Compare JavaScript's reference passing with Go's pointer passing. When should you use value receivers vs pointer receivers?
6. **Memory Management Exercises**: Write a Go program demonstrating how pointers affect performance. Compare pass-by-value vs pass-by-pointer for large structs.
7. **Pointer Pitfalls Exercises**: Identify and fix these common pointer traps: nil pointer dereference, pointer issues in loops, incorrect pointer comparisons.

### Project Ideas:
* **Shape Calculator**: Build a simple shape calculator in Go that uses interfaces to handle different geometric shapes (Circle, Rectangle, Triangle). Implement area and perimeter calculations, and use type switches to handle different shape types. Compare this with a JavaScript implementation using classes and inheritance.
* **Memory Management System**: Create a simple memory pool management system demonstrating pointer usage. Include object creation, recycling, and reuse, comparing pointer vs value type memory efficiency.
* **Linked List Data Structure**: Implement a doubly-linked list making full use of Go's pointer features. Include insert, delete, and traverse operations, and compare performance with JavaScript's object reference implementation.

### Next Steps:
* Learn about Go's concurrency features with goroutines and channels
* Explore Go's error handling patterns and best practices
* Understand Go's package management and module system
